// SPDX-License-Identifier: Apache-2.0 AND MIT


#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <openssl/core_dispatch.h>
#include <openssl/core_names.h>
#include <openssl/provider.h>
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/trace.h>
#include <openssl/params.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/param_build.h>
#include <openssl/bio.h> 
#include <openssl/asn1.h>
#include <openssl/ec.h>
#include <openssl/rsa.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h> 

#include "test_common.h"
#include "tlstest_helpers.h"

int dtls_flag=0;

const char *kHybridSignatureAlgorithms[] = {
    "p256_MLDSA44",
    "rsa3072_MLDSA44",
    "p384_MLDSA65",
    "p521_MLDSA87",
    "p256_falcon512",
    "rsa3072_falcon512",
    "p256_falconpadded512",
    "rsa3072_falconpadded512",
    "p521_falcon1024",
    "p521_falconpadded1024",
    "p256_sphincssha2128fsimple",
    "rsa3072_sphincssha2128fsimple",
    "p256_sphincssha2128ssimple",
    "rsa3072_sphincssha2128ssimple",
    "p384_sphincssha2192fsimple",
    "p256_sphincsshake128fsimple",
    "rsa3072_sphincsshake128fsimple",
    "p256_mayo1",
    "p256_mayo2",
    "p384_mayo3",
    "p521_mayo5",
    "p256_OV_Is_pkc",
    "p256_OV_Ip_pkc",
    "p256_OV_Is_pkc_skc",
    "p256_OV_Ip_pkc_skc",
    "p256_snova2454",
    "p256_snova2454esk",
    "p256_snova37172",
    "p384_snova2455",
    "p521_snova2965",
    NULL,
};

static int is_signature_algorithm_hybrid(const char **list, const char *s) {
    for (; *list != NULL && strcmp(*list, s) != 0; ++list);
    return *list != NULL;
}

static OSSL_LIB_CTX *init_openssl(void) {
    OSSL_LIB_CTX *ctx;

    if (!(ctx = OSSL_LIB_CTX_new())) {
        fprintf(stderr, cRED "failed to initialize a new `OSSL_LIB_CTX` " cNORM "\n");
    }
    return ctx;
}

static int test_oqs_tlssig(OSSL_LIB_CTX *libctx,const char *sig_name, char *key_addr) {
    SSL_CTX *cctx = NULL, *sctx = NULL;
    SSL *clientssl = NULL, *serverssl = NULL;
    int ret = 1, testresult = 0;
    char certpath[300];
    char privkeypath[300];
    const char *sep = "/";


    if (!alg_is_enabled(sig_name)) {
        printf("Not testing disabled algorithm %s.\n", sig_name);
        return 1;
    }

    sprintf(certpath, "%s%s%s%s", key_addr, sep, sig_name, "_srv.crt");
    sprintf(privkeypath, "%s%s%s%s", key_addr, sep, sig_name, "_srv.key");
    /* ensure certsdir exists */
    if (mkdir(key_addr, 0700)) {
        if (errno != EEXIST) {
            fprintf(stderr, "Couldn't create certsdir %s: Err = %d\n", key_addr,
                    errno);
            ret = -1;
            goto err;
        }
    }
    
    if (!create_cert_key(libctx, (char *)sig_name, certpath, privkeypath)) {
        fprintf(stderr, "Cert/keygen failed for %s at %s/%s\n", sig_name,
                certpath, privkeypath);
        ret = -1;
        goto err;
    }

    testresult = create_tls1_3_ctx_pair(libctx, &sctx, &cctx, certpath,
                                        privkeypath, dtls_flag);
    printf("1\n");
    if (!testresult) {
        ret = -1;
        goto err;
    }
    printf("2\n");
    testresult =
        create_tls_objects(sctx, cctx, &serverssl, &clientssl, dtls_flag);

    if (!testresult) {
        ret = -2;
        goto err;
    }
    printf("3\n");
    testresult = create_tls_connection(serverssl, clientssl, SSL_ERROR_NONE);
    if (!testresult) {
        ret = -5;
        goto err;
    }

err:
    SSL_free(serverssl);
    SSL_free(clientssl);
    SSL_CTX_free(sctx);
    SSL_CTX_free(cctx);
    return ret;
}

/* reactivate when EVP_SIGNATURE_do_all_provided doesn't crash any more:
static void test_oqs_sigs(EVP_SIGNATURE *evpsig, void *vp) {
        OSSL_PROVIDER* prov = EVP_SIGNATURE_get0_provider(evpsig);
        if (!strcmp(OSSL_PROVIDER_get0_name(prov), "oqsprovider")) {
                printf("Commencing test of %s:\n",
EVP_SIGNATURE_get0_name(evpsig));
                test_oqs_tlssig(EVP_SIGNATURE_get0_name(evpsig));
        }
}
*/
int main(int argc, char *argv[]) {
  OSSL_LIB_CTX *libctx;
  OSSL_PROVIDER *oqs_provider;
  char key_addr[100] ;
  const char *modulename;
  const char *configfile;
  const OSSL_ALGORITHM *sigalgs;
    int query_nocache;  
    int ret = EXIT_FAILURE;
          
   if (!(libctx = init_openssl())) {
        goto end;
    }
   T(argc == 3);
    modulename = argv[1];
    configfile = argv[2];
    
    if(!(oqs_provider = OSSL_PROVIDER_load(libctx, modulename))){
        fputs(cRED "  `oqs_provider` is NULL " cNORM "\n", stderr);
        goto end;    
    }

   sigalgs = OSSL_PROVIDER_query_operation(oqs_provider, OSSL_OP_SIGNATURE,
                                            &query_nocache);
    
    fprintf(stdout, "Enter the path to load the files:");
        if (fgets(key_addr, sizeof(key_addr), stdin) == NULL) {
        fprintf(stderr, cRED " Failed to read input." cNORM "\n");
                  goto unload_def_provider;
    }
    key_addr[strcspn(key_addr, "\n")] = 0;
    //printf("%s\n", key_addr);  
    
    if (sigalgs) {
       for (; sigalgs->algorithm_names != NULL; sigalgs++) {
                  if (!is_signature_algorithm_hybrid(kHybridSignatureAlgorithms, sigalgs->algorithm_names)) {
           //fprintf(stderr, cRED "%s is not hybrid." cNORM "\n",sigalgs->algorithm_names);
                                continue;
          }
                            if (test_oqs_tlssig(libctx ,sigalgs->algorithm_names, key_addr)) {
           fprintf(stderr, cRED " Failed for %s " cNORM "\n", sigalgs->algorithm_names);
       } else {
           fprintf(stderr, cGREEN "%s succeeded" cNORM "\n", sigalgs->algorithm_names);       
      }
    }
    }
    ret = 0;
unload_def_provider:
    OSSL_PROVIDER_unload(oqs_provider);   
end:
    OSSL_LIB_CTX_free(libctx);
    return ret;
  
}
